// Copyright 2012 Google Inc. All Rights Reserved.

package com.google.appengine.preferencessync.internal;

import com.google.appengine.syncserver.shared.PropertyChange;

import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

/**
 * State of server after the last successful sync in the client.
 */
public class ServerSharedPreferences {

  /**
   * Class for adding, removing, checking namespace for strings.
   */
  public static class Namespacing {
    private final String namespace;

    public Namespacing(String namespacing) {
      this.namespace = namespacing;
    }

    public String getNamespacedString(String str) {
      return namespace + str;
    }

    public boolean hasNamespacing(String str) {
      return str.startsWith(namespace);
    }

    public String removeNamepacing(String str) {
      return str.substring(namespace.length());
    }
  }

  private static final String KEY_NAMESPACE = "u_";
  private static final String METADATA_NAMESPACE = "m_";

  private static final String MAX_VERSION_KEY = "MAX_VERSION";

  private final SharedPreferences sharedPreferences;
  private final Namespacing keyNamespacing = new Namespacing(KEY_NAMESPACE);

  // Namespacing for storing the server head version. We store server head version in the same file
  // because we don't want the version and other server data to be inconsistent.
  private final Namespacing metadataNamespacing = new Namespacing(METADATA_NAMESPACE);

  /**
   * Server {@link SharedPreferences}.
   */
  public ServerSharedPreferences(SharedPreferences preferences) {
    this.sharedPreferences = preferences;
  }

  /**
   * Apply changes to {@link SharedPreferences}.getAll(). Does not make changes to the fields
   * in the class.
   *
   * @param propertyChanges changes to apply to {@link SharedPreferences}.
   * @return A map of preference values.
   */
  public Map<String, String> getMapWithChangesApplied(ArrayList<PropertyChange> propertyChanges) {
    Map<String, String> preferencesMap = getAll();

    for (PropertyChange change : propertyChanges) {
      switch (change.getType()) {
        case DELETE:
          preferencesMap.remove(change.getKey());
          break;
        case UPDATE:
          preferencesMap.put(change.getKey(), change.getValue());
          break;
      }
    }
    return preferencesMap;
  }

  /**
   * Overwrite server copy of shared preference with new copy.
   *
   * @param maxVersion
   * @param newPreferences
   * @throws InternalSyncException
   */
  public void overwriteServerPrefereneces(long maxVersion, SharedPreferences newPreferences)
      throws InternalSyncException {
    Editor editor = sharedPreferences.edit();
    editor.clear();

    editor.putLong(metadataNamespacing.getNamespacedString(MAX_VERSION_KEY), maxVersion);

    Map<String, ?> maps = newPreferences.getAll();
    for (Entry<String, ?> entry : maps.entrySet()) {
      SharedPreferencesUtils.putObjectAsNamespacedString(editor,
          keyNamespacing.getNamespacedString(entry.getKey()), entry.getValue());
    }
    editor.commit();
  }

  /**
   * Get all the SharedPreference values.
   */
  public Map<String, String> getAll() {
    Map<String, String> preferencesMap = new HashMap<String, String>();
    for (Entry<String, ?> entry : sharedPreferences.getAll().entrySet()) {
      if (metadataNamespacing.hasNamespacing(entry.getKey())) {
        continue;
      }
      preferencesMap.put(
          keyNamespacing.removeNamepacing(entry.getKey()), (String) entry.getValue());
    }
    return preferencesMap;
  }

  public long getMaxVersion() {
    return sharedPreferences.getLong(metadataNamespacing.getNamespacedString(MAX_VERSION_KEY), 0);
  }

  public void clear() {
    Editor editor = sharedPreferences.edit();
    editor.clear();
    editor.commit();
  }
}
